    package com.pianzhu.blockchainemr.AOP;

import com.pianzhu.blockchainemr.mapper.OperationLogMapper;
import com.pianzhu.blockchainemr.pojo.OperationLog;
import com.pianzhu.blockchainemr.utils.CurrentHolder;
import com.pianzhu.blockchainemr.utils.CurrentHolderInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * 操作日志切面
 * 自动记录所有Controller的访问日志到数据库
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    @Autowired
    private OperationLogMapper operationLogMapper;

    /*拦截所有Controller的方法*/
    @Pointcut("execution(* com.pianzhu.blockchainemr.controller.*Controller.*(..))")
    public void operationLogPointcut() {}

    /*记录完整的操作日志*/
    @Around("operationLogPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        String status = "SUCCESS";
        String errorMsg = null;

        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            status = "ERROR";
            errorMsg = e.getMessage();
            throw e;
        } finally {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;

            try {
                // 异步保存日志（避免影响主流程性能）
                saveOperationLog(joinPoint, result, status, errorMsg, executionTime);
            } catch (Exception e) {
                // 日志保存失败不影响主流程
                log.error("保存操作日志失败: {}", e.getMessage());
            }
        }

        return result;
    }

    /*保存操作日志到数据库*/
    private void saveOperationLog(ProceedingJoinPoint joinPoint, Object result,
                                  String status, String errorMsg, long executionTime) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes)
                    RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }

            HttpServletRequest request = attributes.getRequest();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            OperationLog operationLog = new OperationLog();

            // 1. 基本信息
            operationLog.setModule(getModuleName(joinPoint));
            operationLog.setOperation(method.getName());
            operationLog.setDescription(getDescription(method));

            // 2. 请求信息
            operationLog.setRequestUrl(request.getRequestURL().toString());
            operationLog.setRequestMethod(request.getMethod());
            operationLog.setRequestParams(getRequestParams(joinPoint, request));

            // 3. 响应信息
            operationLog.setResponseResult(getResponseResult(result));
            operationLog.setExecutionTime(executionTime);
            operationLog.setStatus(status);
            operationLog.setErrorMessage(errorMsg);

            // 4. 操作者信息（从CurrentHolder获取）
            CurrentHolderInfo currentUser = CurrentHolder.getCurrentLocal();
            if (currentUser != null) {
                operationLog.setOperatorId(currentUser.getId());
                operationLog.setOperatorName(currentUser.getUsername());
                operationLog.setOperatorType(currentUser.getUsertype());
            }

            // 5. 网络信息
            operationLog.setIpAddress(getIpAddress(request));
            operationLog.setUserAgent(request.getHeader("User-Agent"));
            operationLog.setCreatedTime(LocalDateTime.now());

            // 保存到数据库
            operationLogMapper.insert(operationLog);

        } catch (Exception e) {
            log.error("构建操作日志异常: {}", e.getMessage(), e);
        }
    }

    private String getModuleName(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        // 移除Controller后缀，例如：AdminController -> Admin
        if (className.endsWith("Controller")) {
            return className.substring(0, className.length() - 10);
        }
        return className;
    }

    private String getDescription(Method method) {
        return method.getName();
    }

    private String getRequestParams(ProceedingJoinPoint joinPoint, HttpServletRequest request) {
        try {
            String queryString = request.getQueryString();
            if (queryString != null && !queryString.isEmpty()) {
                // 限制参数长度，避免数据库字段溢出
                return queryString.length() > 1000 ? queryString.substring(0, 1000) + "..." : queryString;
            }

            // 对于POST JSON等情况，从方法参数获取
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                return "";
            }

            StringBuilder params = new StringBuilder();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            String[] paramNames = signature.getParameterNames();

            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];

                if (arg instanceof HttpServletRequest ||
                        arg instanceof HttpServletResponse ||
                        arg instanceof MultipartFile) {
                    continue;
                }

                if (params.length() > 0) {
                    params.append("&");
                }

                String paramName = (paramNames != null && i < paramNames.length) ?
                        paramNames[i] : "arg" + i;

                if (arg == null) {
                    params.append(paramName).append("=null");
                } else {
                    params.append(paramName).append("=").append(truncateString(arg.toString(), 100));
                }
            }

            return params.toString();

        } catch (Exception e) {
            return "获取参数失败";
        }
    }


    private String getResponseResult(Object result) {
        if (result == null) {
            return "";
        }

        try {
            return truncateString(result.toString(), 2000);
        } catch (Exception e) {
            return "结果转换失败";
        }
    }

    private String truncateString(String str, int maxLength) {
        if (str == null || str.length() <= maxLength) {
            return str;
        }
        return str.substring(0, maxLength) + "...[截断]";
    }

    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 多个代理时，取第一个IP
        if (ip != null && ip.length() > 15 && ip.indexOf(",") > 0) {
            ip = ip.substring(0, ip.indexOf(","));
        }

        return ip;
    }
}